﻿/*
REWRITTEN FROM SCRATCH BY PUSSYWIZARD, IT OWNS NOW!
*/

#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellScript.h"
#include "ulduar.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "PassiveAI.h"
#include "Player.h"
#pragma execution_character_set("utf-8") 

enum HodirSpellData
{
	SPELL_BERSERK						= 26662,

	SPELL_BITING_COLD_BOSS_AURA			= 62038,
	SPELL_BITING_COLD_PLAYER_AURA		= 62039,
	SPELL_BITING_COLD_DAMAGE			= 62188,

	SPELL_FREEZE						= 62469,

	SPELL_FLASH_FREEZE_CAST				= 61968,
	SPELL_FLASH_FREEZE_INSTAKILL		= 62226,
	SPELL_FLASH_FREEZE_TRAPPED_PLAYER	= 61969,
	SPELL_FLASH_FREEZE_TRAPPED_NPC		= 61990,
	SPELL_FLASH_FREEZE_VISUAL			= 62148,
	SPELL_SAFE_AREA						= 65705,
	SPELL_SAFE_AREA_TRIGGERED			= 62464,
	
	SPELL_ICICLE_BOSS_AURA				= 62227,
	SPELL_ICICLE_TBBA					= 63545,

	SPELL_ICICLE_VISUAL_UNPACKED		= 62234,
	SPELL_ICICLE_VISUAL_PACKED			= 62462,
	SPELL_ICICLE_VISUAL_FALLING			= 62453,
	SPELL_ICICLE_FALL_EFFECT_UNPACKED	= 62236,
	SPELL_ICICLE_FALL_EFFECT_PACKED		= 62460,
	SPELL_ICE_SHARDS_SMALL				= 62457,
	SPELL_ICE_SHARDS_BIG				= 65370,
	SPELL_SNOWDRIFT						= 62463,

	SPELL_FROZEN_BLOWS_10				= 62478,
	SPELL_FROZEN_BLOWS_25				= 63512,

	// Helpers:
	SPELL_PRIEST_DISPELL_MAGIC			= 63499,
	SPELL_PRIEST_GREAT_HEAL				= 62809,
	SPELL_PRIEST_SMITE					= 61923,

	SPELL_DRUID_WRATH					= 62793,
	SPELL_DRUID_STARLIGHT_AREA_AURA		= 62807,

	SPELL_SHAMAN_LAVA_BURST				= 61924,
	SPELL_SHAMAN_STORM_CLOUD_10			= 65123,
	SPELL_SHAMAN_STORM_CLOUD_25			= 65133,
	SPELL_SHAMAN_STORM_POWER_10			= 63711,
	SPELL_SHAMAN_STORM_POWER_25			= 65134,

	SPELL_MAGE_FIREBALL					= 61909,
	SPELL_MAGE_MELT_ICE					= 64528,
	SPELL_MAGE_CONJURE_TOASTY_FIRE		= 62823,
	SPELL_MAGE_SUMMON_TOASTY_FIRE		= 62819,
	SPELL_MAGE_TOASTY_FIRE_AURA			= 62821,
	SPELL_SINGED						= 65280,
};

enum HodirNPCs
{
	//NPC_HODIR							= 32845,

	NPC_PAN_FIELD_MEDIC_PENNY			= 32897,
	NPC_DAN_ELLIE_NIGHTFEATHER			= 32901,
	NPC_SAN_ELEMENTALIST_AVUUN			= 32900,
	NPC_MAN_MISSY_FLAMECUFFS			= 32893,

	NPC_PAH_FIELD_MEDIC_JESSI			= 33326,
	NPC_DAH_EIVI_NIGHTFEATHER			= 33325,
	NPC_SAH_ELEMENTALIST_MAHFUUN		= 33328,
	NPC_MAH_SISSY_FLAMECUFFS			= 33327,

	NPC_PHN_BATTLEPRIEST_ELIZA			= 32948,
	NPC_DHN_TOR_GREYCLOUD				= 32941,
	NPC_SHN_SPIRITWALKER_YONA			= 32950,
	NPC_MHN_VEESHA_BLAZEWEAVER			= 32946,

	NPC_PHH_BATTLEPRIEST_GINA			= 33330,
	NPC_DHH_KAR_GREYCLOUD				= 33333,
	NPC_SHH_SPIRITWALKER_TARA			= 33332,
	NPC_MHH_AMIRA_BLAZEWEAVER			= 33331,

	NPC_FLASH_FREEZE_PLR				= 32926,
	NPC_FLASH_FREEZE_NPC				= 32938,
	NPC_ICICLE_UNPACKED					= 33169,
	NPC_ICICLE_PACKED					= 33173,
	NPC_TOASTY_FIRE						= 33342,
};

enum HodirGOs
{
	GO_HODIR_SNOWDRIFT					= 194173,
	// GO_HODIR_FROZEN_DOOR				= 194441,
	// GO_HODIR_DOOR					= 194634,
};

enum HodirEvents
{
	// Hodir:
	EVENT_FLASH_FREEZE					= 1,
	EVENT_FROZEN_BLOWS					= 2,
	EVENT_BERSERK						= 3,
	EVENT_FREEZE						= 4,
	EVENT_SMALL_ICICLES_ENABLE			= 5,
	EVENT_HARD_MODE_MISSED				= 6,

	EVENT_TRY_FREE_HELPER				= 10,
	EVENT_PRIEST_DISPELL_MAGIC			= 11,
	EVENT_PRIEST_GREAT_HEAL				= 12,
	EVENT_PRIEST_SMITE					= 13,
	EVENT_DRUID_WRATH					= 14,
	EVENT_DRUID_STARLIGHT				= 15,
	EVENT_SHAMAN_LAVA_BURST				= 16,
	EVENT_SHAMAN_STORM_CLOUD			= 17,
	EVENT_MAGE_TOASTY_FIRE				= 18,
	EVENT_MAGE_FIREBALL					= 19,
	EVENT_MAGE_MELT_ICE					= 20,
};

#define SPELL_FROZEN_BLOWS				RAID_MODE(SPELL_FROZEN_BLOWS_10, SPELL_FROZEN_BLOWS_25)
#define SPELL_SHAMAN_STORM_CLOUD		RAID_MODE(SPELL_SHAMAN_STORM_CLOUD_10, SPELL_SHAMAN_STORM_CLOUD_25)

#define TEXT_HODIR_AGGRO				"你将为擅闯付出代价!"
#define	TEXTEMOTE_HODIR_FROZEN_BLOWS	"霍迪尔 狂怒地大吼!."
#define TEXT_HODIR_FLASH_FREEZE			"你将被北风所吞噬!"
#define TEXTEMOTE_HODIR_HARD_MODE_MISSED "霍迪尔 打碎了霍迪尔珍贮箱!"
#define TEXT_HODIR_SLAIN_1				"真悲哀.远道而来,只为一败."
#define TEXT_HODIR_SLAIN_2				"欢迎来到无尽的凛冬."
#define TEXT_HODIR_BERSERK				"够了!一切到此为止!"
#define TEXT_HODIR_DEFEATED				"我...我终于从他的掌控中...解脱了."

enum HodirSounds
{
	SOUND_HODIR_AGGRO					= 15552,
	SOUND_HODIR_SLAIN_1					= 15553,
	SOUND_HODIR_SLAIN_2					= 15554,
	SOUND_HODIR_FLASH_FREEZE			= 15555,
	SOUND_HODIR_FROZEN_BLOWS			= 15556,
	SOUND_HODIR_DEFEATED				= 15557,
	SOUND_HODIR_BERSERK					= 15558,
};

struct HodirHelperData
{
	uint32 id;
	float x,y;
};
HodirHelperData hhd[4][4] = {
// Alliance:
{
	{NPC_PAN_FIELD_MEDIC_PENNY, 2020.46f, -236.74f},
	{NPC_DAN_ELLIE_NIGHTFEATHER, 2007.21f, -241.57f},
	{NPC_SAN_ELEMENTALIST_AVUUN, 1999.14f, -230.69f},
	{NPC_MAN_MISSY_FLAMECUFFS, 1984.38f, -242.57f}
},
{
	{NPC_PAH_FIELD_MEDIC_JESSI, 2012.29f, -233.70f},
	{NPC_DAH_EIVI_NIGHTFEATHER, 1995.75f, -241.32f},
	{NPC_SAH_ELEMENTALIST_MAHFUUN, 1989.31f, -234.26f},
	{NPC_MAH_SISSY_FLAMECUFFS, 1977.87f, -233.99f}
},
// Horde:
{
	{NPC_PHN_BATTLEPRIEST_ELIZA, 2020.46f, -236.74f},
	{NPC_DHN_TOR_GREYCLOUD, 2007.21f, -241.57f},
	{NPC_SHN_SPIRITWALKER_YONA, 1999.14f, -230.69f},
	{NPC_MHN_VEESHA_BLAZEWEAVER, 1984.38f, -242.57f}
},
{
	{NPC_PHH_BATTLEPRIEST_GINA, 2012.29f, -233.70f},
	{NPC_DHH_KAR_GREYCLOUD, 1995.75f, -241.32f},
	{NPC_SHH_SPIRITWALKER_TARA, 1989.31f, -234.6f},
	{NPC_MHH_AMIRA_BLAZEWEAVER, 1977.87f, -233.99f}
}
};

class boss_hodir : public CreatureScript
{
public:
	boss_hodir() : CreatureScript("boss_hodir") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new boss_hodirAI (pCreature);
	}

	struct boss_hodirAI : public ScriptedAI
	{
		boss_hodirAI(Creature *pCreature) : ScriptedAI(pCreature), summons(me)
		{
			pInstance = pCreature->GetInstanceScript();
			if (!me->IsAlive())
				if (pInstance)
					pInstance->SetData(TYPE_HODIR, DONE);
		}

		InstanceScript* pInstance;
		EventMap events;
		SummonList summons;
		uint64 Helpers[8];
		bool berserk;
		bool hardmode;
		bool bAchievCheese;
		bool bAchievGettingCold;
		bool bAchievCoolestFriends;
		uint16 addSpawnTimer;

		void Reset()
		{
			events.Reset();
			summons.DespawnAll();
			berserk = false;
			hardmode = true;
			bAchievCheese = true;
			bAchievGettingCold = true;
			bAchievCoolestFriends = true;
			addSpawnTimer = 5000;

			if (pInstance && pInstance->GetData(TYPE_HODIR) != DONE)
				pInstance->SetData(TYPE_HODIR, NOT_STARTED);
		}

		void EnterCombat(Unit* pWho)
		{
			if (summons.size() != RAID_MODE(8, 16))
			{
				EnterEvadeMode();
				return;
			}
			me->setActive(true);
			me->CastSpell(me, SPELL_BITING_COLD_BOSS_AURA, true);
			SmallIcicles(true);
			events.Reset();
			events.RescheduleEvent(EVENT_FLASH_FREEZE, 60000);
			events.RescheduleEvent(EVENT_FREEZE, 15000);
			events.RescheduleEvent(EVENT_BERSERK, 480000);
			events.RescheduleEvent(EVENT_HARD_MODE_MISSED, 180000);

			me->MonsterYell(TEXT_HODIR_AGGRO, LANG_UNIVERSAL, 0);
			me->PlayDirectSound(SOUND_HODIR_AGGRO, 0);

			if (pInstance && pInstance->GetData(TYPE_HODIR) != DONE)
				pInstance->SetData(TYPE_HODIR, IN_PROGRESS);
		}

		void JustReachedHome() { me->setActive(false); }

		void SmallIcicles(bool enable)
		{
			if( enable )
				me->CastSpell(me, SPELL_ICICLE_BOSS_AURA, true);
			else
				me->RemoveAura(SPELL_ICICLE_BOSS_AURA);
		}

		void SpellHitTarget(Unit* target, const SpellInfo* spell)
		{
			switch( spell->Id )
			{
				case SPELL_ICICLE_TBBA:
					me->CastSpell(target, SPELL_ICICLE_VISUAL_UNPACKED, true);
					break;
				case SPELL_FLASH_FREEZE_VISUAL:
					{
						std::list<Creature*> fires;
						me->GetCreaturesWithEntryInRange(fires, 200.0f, NPC_TOASTY_FIRE);
						for (std::list<Creature*>::iterator itr = fires.begin(); itr != fires.end(); ++itr)
							(*itr)->AI()->DoAction(1); // remove it
					}
					break;
			}
		}

		void DamageTaken(Unit*, uint32 &damage, DamageEffectType, SpellSchoolMask)
		{
			if (damage >= me->GetHealth() || me->GetHealth() < 150000)
			{
				damage = 0;
				me->SetReactState(REACT_PASSIVE);
				if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
				{
					if (pInstance)
					{
						pInstance->SetData(TYPE_HODIR, DONE);
						me->CastSpell(me, 64899, true); // credit
					}

					me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
					me->setFaction(35);
					me->GetMotionMaster()->Clear();
					me->AttackStop();
					me->CombatStop();
					me->InterruptNonMeleeSpells(true);
					me->RemoveAllAuras();
					pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_BITING_COLD_PLAYER_AURA);

					events.Reset();
					summons.DespawnAll();

					if( GameObject* d = me->FindNearestGameObject(GO_HODIR_FROZEN_DOOR, 250.0f) )
						if( d->GetGoState() != GO_STATE_ACTIVE )
						{
							d->SetLootState(GO_READY);
							d->UseDoorOrButton(0, false);
						}
					if( GameObject* d = me->FindNearestGameObject(GO_HODIR_DOOR, 250.0f) )
						if( d->GetGoState() != GO_STATE_ACTIVE )
						{
							d->SetLootState(GO_READY);
							d->UseDoorOrButton(0, false);
						}

					me->MonsterYell(TEXT_HODIR_DEFEATED, LANG_UNIVERSAL, 0);
					me->PlayDirectSound(SOUND_HODIR_DEFEATED, 0);
					me->DespawnOrUnsummon(10000);

					// spawn appropriate chests
					uint32 chestId = me->GetMap()->Is25ManRaid() ? GO_HODIR_CHEST_NORMAL_HERO : GO_HODIR_CHEST_NORMAL;
					if( GameObject *go = me->SummonGameObject(chestId, 1969.115f, -212.94f, 432.687f, 3*M_PI/2, 0, 0, 0, 0, 0) )
						go->SetUInt32Value(GAMEOBJECT_FLAGS, 0);
					
					if( hardmode )
					{
						uint32 chestId2 = me->GetMap()->Is25ManRaid() ? GO_HODIR_CHEST_HARD_HERO : GO_HODIR_CHEST_HARD;
						if( GameObject *go = me->SummonGameObject(chestId2, 2031.207f, -213.236f, 432.687f, 3*M_PI/2, 0, 0, 0, 0, 0) )
							go->SetUInt32Value(GAMEOBJECT_FLAGS, 0);
					}
				}
			}
		}


		void UpdateAI(uint32 diff)
		{
			if (addSpawnTimer <= diff)
			{
				addSpawnTimer = 5000;
				if (!me->IsInCombat() && !summons.size() && !me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
					if (SelectTargetFromPlayerList(135.0f))
						SpawnHelpers();
			}
			else
				addSpawnTimer -= diff;

			if (!UpdateVictim())
			{
				if (me->IsInCombat())
				{
					Map::PlayerList const& pl = me->GetMap()->GetPlayers();
					for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
						itr->GetSource()->CastSpell(itr->GetSource(), SPELL_FLASH_FREEZE_INSTAKILL, true);
					EnterEvadeMode();
				}
				return;
			}

			if( !berserk && (me->GetPositionX() < 1940.0f || me->GetPositionX() > 2070.0f || me->GetPositionY() < -300.0f || me->GetPositionY() > -155.0f) )
				events.RescheduleEvent(EVENT_BERSERK, 1);

			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;
			
			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_BERSERK:
					{
						berserk = true;
						me->CastSpell(me, SPELL_BERSERK, true);
						me->MonsterYell(TEXT_HODIR_BERSERK, LANG_UNIVERSAL, 0);
						me->PlayDirectSound(SOUND_HODIR_BERSERK, 0);
						events.PopEvent();
					}
					break;
				case EVENT_HARD_MODE_MISSED:
					{
						hardmode = false;
						me->MonsterTextEmote(TEXTEMOTE_HODIR_HARD_MODE_MISSED, 0);
						events.PopEvent();
					}
					break;
				case EVENT_FLASH_FREEZE:
					{
						std::list<Unit*> targets;
						Map::PlayerList const& pl = me->GetMap()->GetPlayers();
						for (Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
							targets.push_back(itr->GetSource());
						targets.remove_if(Trinity::ObjectTypeIdCheck(TYPEID_PLAYER, false));
						targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_FLASH_FREEZE_TRAPPED_PLAYER));
						Trinity::Containers::RandomResizeList(targets, 2);
						for (std::list<Unit*>::const_iterator itr = targets.begin(); itr != targets.end(); ++itr)
						{
							float prevZ = (*itr)->GetPositionZ();
							(*itr)->m_positionZ = 432.7f;
							(*itr)->CastSpell((*itr), SPELL_ICICLE_VISUAL_PACKED, true);
							(*itr)->m_positionZ = prevZ;
						}

						me->CastSpell((Unit*)NULL, SPELL_FLASH_FREEZE_CAST, false);
						me->MonsterTextEmote("霍迪尔 开始施展闪霜!", 0, true);
						me->MonsterYell(TEXT_HODIR_FLASH_FREEZE, LANG_UNIVERSAL, 0);
						me->PlayDirectSound(SOUND_HODIR_FLASH_FREEZE, 0);
						SmallIcicles(false);
						events.RepeatEvent(55000 + urand(0,10000));
						events.ScheduleEvent(EVENT_SMALL_ICICLES_ENABLE, 12000);
						events.ScheduleEvent(EVENT_FROZEN_BLOWS, 15000);
						events.RescheduleEvent(EVENT_FREEZE, 20000);
					}
					break;
				case EVENT_SMALL_ICICLES_ENABLE:
					{
						SmallIcicles(true);
						events.PopEvent();
					}
					break;
				case EVENT_FROZEN_BLOWS:
					{
						me->MonsterTextEmote("霍迪尔 获得冰冻痛击!", 0, true);
						me->MonsterTextEmote(TEXTEMOTE_HODIR_FROZEN_BLOWS, 0);
						me->PlayDirectSound(SOUND_HODIR_FROZEN_BLOWS, 0);
						me->CastSpell(me, SPELL_FROZEN_BLOWS, true);
						events.PopEvent();
					}
					break;
				case EVENT_FREEZE:
					if (Player* plr = SelectTargetFromPlayerList(50.0f, SPELL_FLASH_FREEZE_TRAPPED_PLAYER))
						me->CastSpell(plr, SPELL_FREEZE, false);
					else if (Unit* plr = SelectTarget(SELECT_TARGET_RANDOM, 0, 50.0f, true))
						me->CastSpell(plr, SPELL_FREEZE, false);
					events.RepeatEvent(15000);
					break;
			}

			DoMeleeAttackIfReady();
		}

		Creature* GetHelper(uint8 index)
		{
			return (Helpers[index] ? ObjectAccessor::GetCreature(*me, Helpers[index]) : NULL);
		}

		void SpawnHelpers()
		{
			char faction = 'A';
			if( hhd[0][0].id )
			{
				Map::PlayerList const &cl = me->GetMap()->GetPlayers();
				for (Map::PlayerList::const_iterator itr = cl.begin(); itr != cl.end(); ++itr)
					if (!itr->GetSource()->IsGameMaster())
					{
						faction = (itr->GetSource()->GetTeamId() == TEAM_ALLIANCE ? 'A' : 'H');
						break;
					}
			}

			uint8 cnt = 0;
			if( faction )
				for( uint8 k=0; k<4; ++k )
				{
					if( faction == 'A' && ( k>1 || k==1 && RAID_MODE(1,0) ) ||
						faction == 'H' && ( k<2 || k==3 && RAID_MODE(1,0) ) )
						continue;

					for( uint8 i=0; i<4; ++i )
					{
						if( !hhd[k][i].id )
							continue;

						if( Creature* h_p = me->SummonCreature(hhd[k][i].id, hhd[k][i].x, hhd[k][i].y, 432.69f, M_PI/2) )
						{
							h_p->setFaction(1665);
							if( cnt < 8 )
								Helpers[cnt++] = h_p->GetGUID();

							if( Creature* c = h_p->SummonCreature(NPC_FLASH_FREEZE_NPC, h_p->GetPositionX(), h_p->GetPositionY(), h_p->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000) )
							{
								c->CastSpell(h_p, SPELL_FLASH_FREEZE_TRAPPED_NPC, true);
								JustSummoned(c);
							}
						}
					}
				}
		}

		void KilledUnit(Unit* who)
		{
			if( who->GetTypeId() == TYPEID_PLAYER )
			{
				if( urand(0,1) )
				{
					me->MonsterYell(TEXT_HODIR_SLAIN_1, LANG_UNIVERSAL, 0);
					me->PlayDirectSound(SOUND_HODIR_SLAIN_1, 0);
				}
				else
				{
					me->MonsterYell(TEXT_HODIR_SLAIN_2, LANG_UNIVERSAL, 0);
					me->PlayDirectSound(SOUND_HODIR_SLAIN_2, 0);
				}
			}
		}

		void JustSummoned(Creature* s)
		{
			summons.Summon(s);
		}

		void SummonedCreatureDespawn(Creature* s)
		{
			summons.Despawn(s);
		}

		bool CanAIAttack(const Unit* t) const
		{
			if (t->GetTypeId() == TYPEID_PLAYER)
				return !t->HasAura(SPELL_FLASH_FREEZE_TRAPPED_PLAYER);
			else if (t->GetTypeId() == TYPEID_UNIT)
				return !t->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC);

			return true;
		}

		void SetData(uint32 id, uint32 value)
		{
			if (value)
				switch (id)
				{
					case 1: bAchievCheese = false; break;
					case 2: bAchievGettingCold = false; break;
					case 4: bAchievCoolestFriends = false; break;
				}
		}

		uint32 GetData(uint32 id) const
		{
			switch (id)
			{
				case 1: return (bAchievCheese ? 1 : 0);
				case 2: return (bAchievGettingCold ? 1 : 0);
				case 3: return (hardmode ? 1 : 0);
				case 4: return (bAchievCoolestFriends ? 1 : 0);
			}
			return 0;
		}

		void MoveInLineOfSight(Unit* who) {}
	};
};

class npc_ulduar_icicle : public CreatureScript
{
public:
	npc_ulduar_icicle() : CreatureScript("npc_ulduar_icicle") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_icicleAI (pCreature);
	}

	struct npc_ulduar_icicleAI : public NullCreatureAI
	{
		npc_ulduar_icicleAI(Creature *pCreature) : NullCreatureAI(pCreature)
		{
			timer1 = 2000;
			timer2 = 5000;
		}

		uint16 timer1;
		uint16 timer2;

		void UpdateAI(uint32 diff)
		{
			if( timer1 <= diff )
			{
				me->CastSpell(me, (me->GetEntry()==33169 ? SPELL_ICICLE_FALL_EFFECT_UNPACKED : SPELL_ICICLE_FALL_EFFECT_PACKED), true);
				me->CastSpell(me, SPELL_ICICLE_VISUAL_FALLING, false);
				timer1 = 60000;
			}
			else
				timer1 -= diff;

			if (timer2 <= diff)
			{
				me->SetDisplayId(11686);
				timer2 = 60000;
			}
			else
				timer2 -= diff;
		}
	};
};

class npc_ulduar_flash_freeze : public CreatureScript
{
public:
	npc_ulduar_flash_freeze() : CreatureScript("npc_ulduar_flash_freeze") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_flash_freezeAI (pCreature);
	}

	struct npc_ulduar_flash_freezeAI : public NullCreatureAI
	{
		npc_ulduar_flash_freezeAI(Creature *pCreature) : NullCreatureAI(pCreature)
		{
			timer = 2500;
			pInstance = me->GetInstanceScript();
		}

		InstanceScript* pInstance;
		uint16 timer;

		void DamageTaken(Unit* doneBy, uint32 &damage, DamageEffectType, SpellSchoolMask)
		{
			if (pInstance && doneBy)
				if (pInstance->GetData(TYPE_HODIR) == NOT_STARTED)
					if (Creature* hodir = ObjectAccessor::GetCreature(*me, pInstance->GetData64(TYPE_HODIR)))
						hodir->AI()->AttackStart(doneBy);
		}

		void UpdateAI(uint32 diff)
		{
			if (timer <= diff)
			{
				timer = 2500;
				if (me->IsSummon())
					if (Unit* s = me->ToTempSummon()->GetSummoner())
					{
						if (s->GetTypeId() == TYPEID_PLAYER && !s->HasAura(SPELL_FLASH_FREEZE_TRAPPED_PLAYER) || s->GetTypeId() == TYPEID_UNIT && !s->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC))
							me->DespawnOrUnsummon(2000);
						else if (s->GetTypeId() == TYPEID_PLAYER)
							if (InstanceScript* pInstance = me->GetInstanceScript())
								if (pInstance->GetData(TYPE_HODIR) == NOT_STARTED)
								{
									s->CastSpell(s, SPELL_FLASH_FREEZE_INSTAKILL, true);
									me->DespawnOrUnsummon(2000);
								}
					}
					else
						me->DespawnOrUnsummon(2000);
			}
			else
				timer -= diff;
		}
	};
};

class npc_ulduar_toasty_fire : public CreatureScript
{
public:
	npc_ulduar_toasty_fire() : CreatureScript("npc_ulduar_toasty_fire") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_toasty_fireAI (pCreature);
	}

	struct npc_ulduar_toasty_fireAI : public NullCreatureAI
	{
		npc_ulduar_toasty_fireAI(Creature *pCreature) : NullCreatureAI(pCreature)
		{
			me->CastSpell(me, SPELL_MAGE_TOASTY_FIRE_AURA, true);
		}

		void DoAction(int32 a)
		{
			if (a == 1)
			{
				if( GameObject* fire = me->FindNearestGameObject(194300, 1.0f) )
				{
					fire->SetOwnerGUID(0);
					fire->Delete();
				}
				me->DespawnOrUnsummon(); // this will remove DynObjects
			}
		}

		void SpellHit(Unit* caster, const SpellInfo* spell)
		{
			switch( spell->Id )
			{
				case SPELL_ICE_SHARDS_SMALL:
				case SPELL_ICE_SHARDS_BIG:
					DoAction(1);
					break;
			}
		}
	};
};

class npc_ulduar_hodir_priest : public CreatureScript
{
public:
	npc_ulduar_hodir_priest() : CreatureScript("npc_ulduar_hodir_priest") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_hodir_priestAI (pCreature);
	}

	struct npc_ulduar_hodir_priestAI : public ScriptedAI
	{
		npc_ulduar_hodir_priestAI(Creature *pCreature) : ScriptedAI(pCreature)
		{
			pInstance = me->GetInstanceScript();
			events.Reset();
			me->SetReactState(REACT_PASSIVE);
		}

		EventMap events;
		InstanceScript* pInstance;

		void AttackStart(Unit* who)
		{
			AttackStartCaster(who, 17.0f);
		}

		void ScheduleAbilities()
		{
			events.ScheduleEvent(EVENT_PRIEST_DISPELL_MAGIC, 7000);
			events.ScheduleEvent(EVENT_PRIEST_GREAT_HEAL, urand(6000,7000));
			events.ScheduleEvent(EVENT_PRIEST_SMITE, 2100);
		}

		void SpellHit(Unit* /*caster*/, const SpellInfo* spell)
		{
			if(spell->Id == SPELL_FLASH_FREEZE_TRAPPED_NPC)
			{
				events.Reset();
				events.ScheduleEvent(EVENT_TRY_FREE_HELPER, 2000);
			}
		}

		void UpdateAI(uint32 diff)
		{
			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_TRY_FREE_HELPER:
					{
						if( !me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC) )
							if( pInstance )
								if( uint64 g = pInstance->GetData64(TYPE_HODIR) )
									if( Creature* hodir = ObjectAccessor::GetCreature(*me, g) )
									{
										AttackStart(hodir);
										events.PopEvent();
										ScheduleAbilities();
										break;
									}
						events.RepeatEvent(2000);
					}
					break;
				case EVENT_PRIEST_DISPELL_MAGIC:
					me->CastCustomSpell(SPELL_PRIEST_DISPELL_MAGIC, SPELLVALUE_MAX_TARGETS, 1, (Unit*)NULL, false);
					events.RepeatEvent(7000);
					break;
				case EVENT_PRIEST_GREAT_HEAL:
					me->CastSpell(me, SPELL_PRIEST_GREAT_HEAL, false);
					events.RepeatEvent(urand(6000,7000));
					break;
				case EVENT_PRIEST_SMITE:
					if (Unit* victim = me->GetVictim())
						me->CastSpell(victim, SPELL_PRIEST_SMITE, false);
					events.RepeatEvent(2100);
					break;
			}
		}

		void MoveInLineOfSight(Unit* who) {}
		void EnterEvadeMode() {}
		bool CanAIAttack(const Unit* t) const { return t->GetEntry() == NPC_HODIR; }

		void JustDied(Unit* /*killer*/)
		{
			if (pInstance)
				if (Creature* hodir = pInstance->instance->GetCreature(pInstance->GetData64(TYPE_HODIR)))
					hodir->AI()->SetData(4, 1);
		}
	};
};

class npc_ulduar_hodir_druid : public CreatureScript
{
public:
	npc_ulduar_hodir_druid() : CreatureScript("npc_ulduar_hodir_druid") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_hodir_druidAI (pCreature);
	}

	struct npc_ulduar_hodir_druidAI : public ScriptedAI
	{
		npc_ulduar_hodir_druidAI(Creature *pCreature) : ScriptedAI(pCreature)
		{
			pInstance = me->GetInstanceScript();
			events.Reset();
			me->SetReactState(REACT_PASSIVE);
		}

		EventMap events;
		InstanceScript* pInstance;

		void AttackStart(Unit* who)
		{
			AttackStartCaster(who, 22.0f);
		}

		void ScheduleAbilities()
		{
			events.ScheduleEvent(EVENT_DRUID_WRATH, 1600);
			events.ScheduleEvent(EVENT_DRUID_STARLIGHT, 10000);
		}

		void SpellHit(Unit* /*caster*/, const SpellInfo* spell)
		{
			if(spell->Id == SPELL_FLASH_FREEZE_TRAPPED_NPC)
			{
				events.Reset();
				events.ScheduleEvent(EVENT_TRY_FREE_HELPER, 2000);
			}
		}

		void UpdateAI(uint32 diff)
		{
			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_TRY_FREE_HELPER:
					{
						if( !me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC) )
							if( pInstance )
								if( uint64 g = pInstance->GetData64(TYPE_HODIR) )
									if( Creature* hodir = ObjectAccessor::GetCreature(*me, g) )
									{
										AttackStart(hodir);
										events.PopEvent();
										ScheduleAbilities();
										break;
									}
						events.RepeatEvent(2000);
					}
					break;
				case EVENT_DRUID_WRATH:
					if (Unit* victim = me->GetVictim())
						me->CastSpell(victim, SPELL_DRUID_WRATH, false);
					events.RepeatEvent(1600);
					break;
				case EVENT_DRUID_STARLIGHT:
					if (me->GetPositionZ() < 433.0f) // ensure npc is on the ground
					{
						me->CastSpell(me, SPELL_DRUID_STARLIGHT_AREA_AURA, false);
						events.RepeatEvent(15000);
						break;
					}
					events.RepeatEvent(3000);
					break;
			}
		}

		void MoveInLineOfSight(Unit* who) {}
		void EnterEvadeMode() {}
		bool CanAIAttack(const Unit* t) const { return t->GetEntry() == NPC_HODIR; }

		void JustDied(Unit* /*killer*/)
		{
			if (pInstance)
				if (Creature* hodir = pInstance->instance->GetCreature(pInstance->GetData64(TYPE_HODIR)))
					hodir->AI()->SetData(4, 1);
		}
	};
};

class npc_ulduar_hodir_shaman : public CreatureScript
{
public:
	npc_ulduar_hodir_shaman() : CreatureScript("npc_ulduar_hodir_shaman") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_hodir_shamanAI (pCreature);
	}

	struct npc_ulduar_hodir_shamanAI : public ScriptedAI
	{
		npc_ulduar_hodir_shamanAI(Creature *pCreature) : ScriptedAI(pCreature)
		{
			pInstance = me->GetInstanceScript();
			events.Reset();
			me->SetReactState(REACT_PASSIVE);
		}

		EventMap events;
		InstanceScript* pInstance;

		void AttackStart(Unit* who)
		{
			AttackStartCaster(who, 25.0f);
		}

		void ScheduleAbilities()
		{
			events.ScheduleEvent(EVENT_SHAMAN_LAVA_BURST, 2600);
			events.ScheduleEvent(EVENT_SHAMAN_STORM_CLOUD, 10000);
		}

		void SpellHit(Unit* /*caster*/, const SpellInfo* spell)
		{
			if(spell->Id == SPELL_FLASH_FREEZE_TRAPPED_NPC)
			{
				events.Reset();
				events.ScheduleEvent(EVENT_TRY_FREE_HELPER, 2000);
			}
		}

		void SpellHitTarget(Unit* target, const SpellInfo* spell)
		{
			if (target && spell->Id == SPELL_SHAMAN_STORM_CLOUD)
				if (Aura* a = target->GetAura(SPELL_SHAMAN_STORM_CLOUD, me->GetGUID()))
					a->SetStackAmount(spell->StackAmount);
		}

		void UpdateAI(uint32 diff)
		{
			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_TRY_FREE_HELPER:
					{
						if( !me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC) )
							if( pInstance )
								if( uint64 g = pInstance->GetData64(TYPE_HODIR) )
									if( Creature* hodir = ObjectAccessor::GetCreature(*me, g) )
									{
										AttackStart(hodir);
										events.PopEvent();
										ScheduleAbilities();
										break;
									}
						events.RepeatEvent(2000);
					}
					break;
				case EVENT_SHAMAN_LAVA_BURST:
					if (Unit* victim = me->GetVictim())
						me->CastSpell(victim, SPELL_SHAMAN_LAVA_BURST, false);
					events.RepeatEvent(2600);
					break;
				case EVENT_SHAMAN_STORM_CLOUD:
					if (Player* target = ScriptedAI::SelectTargetFromPlayerList(35.0f, SPELL_SHAMAN_STORM_CLOUD))
						me->CastSpell(target, SPELL_SHAMAN_STORM_CLOUD, false);
					events.RepeatEvent(30000);
					break;
			}
		}

		void MoveInLineOfSight(Unit* who) {}
		void EnterEvadeMode() {}
		bool CanAIAttack(const Unit* t) const { return t->GetEntry() == NPC_HODIR; }

		void JustDied(Unit* /*killer*/)
		{
			if (pInstance)
				if (Creature* hodir = pInstance->instance->GetCreature(pInstance->GetData64(TYPE_HODIR)))
					hodir->AI()->SetData(4, 1);
		}
	};
};

class npc_ulduar_hodir_mage : public CreatureScript
{
public:
	npc_ulduar_hodir_mage() : CreatureScript("npc_ulduar_hodir_mage") { }

	CreatureAI* GetAI(Creature* pCreature) const
	{
		return new npc_ulduar_hodir_mageAI (pCreature);
	}

	struct npc_ulduar_hodir_mageAI : public ScriptedAI
	{
		npc_ulduar_hodir_mageAI(Creature *pCreature) : ScriptedAI(pCreature)
		{
			pInstance = me->GetInstanceScript();
			events.Reset();
			me->SetReactState(REACT_PASSIVE);
		}

		EventMap events;
		InstanceScript* pInstance;

		void AttackStart(Unit* who)
		{
			AttackStartCaster(who, 30.0f);
		}

		void ScheduleAbilities()
		{
			events.ScheduleEvent(EVENT_MAGE_FIREBALL, 3100);
			events.ScheduleEvent(EVENT_MAGE_TOASTY_FIRE, 6000);
			events.ScheduleEvent(EVENT_MAGE_MELT_ICE, 1000);
		}

		void SpellHit(Unit* /*caster*/, const SpellInfo* spell)
		{
			if(spell->Id == SPELL_FLASH_FREEZE_TRAPPED_NPC)
			{
				events.Reset();
				events.ScheduleEvent(EVENT_TRY_FREE_HELPER, 2000);
			}
		}

		void UpdateAI(uint32 diff)
		{
			events.Update(diff);

			if( me->HasUnitState(UNIT_STATE_CASTING) )
				return;

			switch( events.GetEvent() )
			{
				case 0:
					break;
				case EVENT_TRY_FREE_HELPER:
					{
						if( !me->HasAura(SPELL_FLASH_FREEZE_TRAPPED_NPC) )
							if( pInstance )
								if( uint64 g = pInstance->GetData64(TYPE_HODIR) )
									if( Creature* hodir = ObjectAccessor::GetCreature(*me, g) )
									{
										AttackStart(hodir);
										events.PopEvent();
										ScheduleAbilities();
										break;
									}
						events.RepeatEvent(2000);
					}
					break;
				case EVENT_MAGE_FIREBALL:
					if (Unit* victim = me->GetVictim())
						me->CastSpell(victim, SPELL_MAGE_FIREBALL, false);
					events.RepeatEvent(3100);
					break;
				case EVENT_MAGE_TOASTY_FIRE:
					me->CastSpell(me, SPELL_MAGE_CONJURE_TOASTY_FIRE, false);
					events.RepeatEvent(10000);
					break;
				case EVENT_MAGE_MELT_ICE:
					{
						std::list<Creature*> FB;
						bool found = false;
						me->GetCreaturesWithEntryInRange(FB, 150.0f, NPC_FLASH_FREEZE_NPC);
						for( std::list<Creature*>::const_iterator itr = FB.begin(); itr != FB.end(); ++itr )
							if( !((*itr)->HasAura(SPELL_MAGE_MELT_ICE)) )
							{
								me->CastSpell((*itr), SPELL_MAGE_MELT_ICE, false);
								found = true;
								break;
							}

						if( found )
						{
							events.DelayEvents(2000);
							events.RepeatEvent(1999);
							break;
						}
						events.RepeatEvent(5000);
					}
					break;
			}
		}

		void MoveInLineOfSight(Unit* who) {}
		void EnterEvadeMode() {}
		bool CanAIAttack(const Unit* t) const { return t->GetEntry() == NPC_HODIR; }

		void JustDied(Unit* /*killer*/)
		{
			if (pInstance)
				if (Creature* hodir = pInstance->instance->GetCreature(pInstance->GetData64(TYPE_HODIR)))
					hodir->AI()->SetData(4, 1);
		}
	};
};

class spell_hodir_biting_cold_main_aura : public SpellScriptLoader
{
public:
	spell_hodir_biting_cold_main_aura() : SpellScriptLoader("spell_hodir_biting_cold_main_aura") { }

	class spell_hodir_biting_cold_main_aura_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_biting_cold_main_aura_AuraScript)

		void HandleEffectPeriodic(AuraEffect const * aurEff)
		{
			if ((aurEff->GetTickNumber()%4) == 0)
				if (Unit* target = GetTarget())
					if (target->GetTypeId() == TYPEID_PLAYER && !target->isMoving() && !target->HasAura(SPELL_BITING_COLD_PLAYER_AURA))
						target->CastSpell(target, SPELL_BITING_COLD_PLAYER_AURA, true);
		}

		void Register()
		{
			OnEffectPeriodic += AuraEffectPeriodicFn(spell_hodir_biting_cold_main_aura_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
		}
	};

	AuraScript *GetAuraScript() const
	{
		return new spell_hodir_biting_cold_main_aura_AuraScript();
	}
};

class spell_hodir_biting_cold_player_aura : public SpellScriptLoader
{
public:
	spell_hodir_biting_cold_player_aura() : SpellScriptLoader("spell_hodir_biting_cold_player_aura") { }

	class spell_hodir_biting_cold_player_aura_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_biting_cold_player_aura_AuraScript)

		uint8 counter;
		bool prev;

		bool Load()
		{
			counter = 0;
			prev = false;
			return true;
		}

		void HandleEffectPeriodic(AuraEffect const * aurEff)
		{
			
			if (Unit* target = GetTarget())
			{
				if (target->GetMapId() == 603)
					SetDuration(GetMaxDuration());
				if (target->HasAura(SPELL_FLASH_FREEZE_TRAPPED_PLAYER))
					return;
				if (target->isMoving() || target->HasAura(SPELL_MAGE_TOASTY_FIRE_AURA))
				{
					if (prev)
					{
						ModStackAmount(-1);
						prev = false;
					}
					else
						prev = true;

					if (counter>=2)
						counter -= 2;
					else if (counter)
						--counter;
				}
				else
				{
					prev = false;
					++counter;
					if (counter >= 4)
					{
						if (GetStackAmount() == 2) // increasing from 2 to 3 (not checking >= to improve performance)
							if (InstanceScript* pInstance = target->GetInstanceScript())
								if (Creature* hodir = pInstance->instance->GetCreature(pInstance->GetData64(TYPE_HODIR)))
									hodir->AI()->SetData(2, 1);
						ModStackAmount(1);
						counter = 0;
					}
				}

				const int32 dmg = 200*pow(2.0f, GetStackAmount());
				target->CastCustomSpell(target, SPELL_BITING_COLD_DAMAGE, &dmg, 0, 0, true);
			}
		}

		void Register()
		{
			OnEffectPeriodic += AuraEffectPeriodicFn(spell_hodir_biting_cold_player_aura_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
		}
	};

	AuraScript *GetAuraScript() const
	{
		return new spell_hodir_biting_cold_player_aura_AuraScript();
	}
};

class spell_hodir_periodic_icicle : public SpellScriptLoader
{
public:
	spell_hodir_periodic_icicle() : SpellScriptLoader("spell_hodir_periodic_icicle") { }

	class spell_hodir_periodic_icicle_SpellScript : public SpellScript
	{
		PrepareSpellScript(spell_hodir_periodic_icicle_SpellScript);

		void FilterTargets(std::list<WorldObject*>& targets)
		{
			targets.remove_if(Trinity::ObjectTypeIdCheck(TYPEID_PLAYER, false));
			targets.remove_if(Trinity::UnitAuraCheck(true, SPELL_FLASH_FREEZE_TRAPPED_PLAYER));
			Trinity::Containers::RandomResizeList(targets, 1);
		}

		void Register()
		{
			OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hodir_periodic_icicle_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
		}
	};

	SpellScript* GetSpellScript() const
	{
		return new spell_hodir_periodic_icicle_SpellScript();
	}
};

class FlashFreezeCheck
{
public:
	FlashFreezeCheck() { }

	bool operator()(WorldObject* target) const
	{
		if (Unit* unit = target->ToUnit())
			return unit->HasAura(SPELL_SAFE_AREA_TRIGGERED) || unit->IsPet();
		return true;
	}
};

class spell_hodir_flash_freeze : public SpellScriptLoader
{
public:
	spell_hodir_flash_freeze() : SpellScriptLoader("spell_hodir_flash_freeze") { }

	class spell_hodir_flash_freeze_SpellScript : public SpellScript
	{
		PrepareSpellScript(spell_hodir_flash_freeze_SpellScript);

		void FilterTargets(std::list<WorldObject*>& targets)
		{
			targets.remove_if(FlashFreezeCheck());
		}

		void Register()
		{
			OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hodir_flash_freeze_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
			OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_hodir_flash_freeze_SpellScript::FilterTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
		}
	};

	class spell_hodir_flash_freeze_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_flash_freeze_AuraScript)

		void HandleEffectPeriodic(AuraEffect const * aurEff)
		{
			if (aurEff->GetTickNumber() == aurEff->GetTotalTicks()-1)
			{
				Unit* target = GetTarget();
				Unit* caster = GetCaster();
				if (!target || !caster || caster->GetTypeId() != TYPEID_UNIT)
					return;

				if (Aura* aur = target->GetAura(target->GetTypeId() == TYPEID_PLAYER ? SPELL_FLASH_FREEZE_TRAPPED_PLAYER : SPELL_FLASH_FREEZE_TRAPPED_NPC))
				{
					if (Unit* caster = aur->GetCaster())
						if (caster->GetTypeId() == TYPEID_UNIT)
							caster->ToCreature()->DespawnOrUnsummon();
					target->CastSpell(target, SPELL_FLASH_FREEZE_INSTAKILL, true);
					return;
				}
				if (target->GetTypeId() == TYPEID_PLAYER)
				{
					caster->ToCreature()->AI()->SetData(1, 1);
					if( Creature* c = target->SummonCreature(NPC_FLASH_FREEZE_PLR, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 5*60*1000) )
					{
						c->CastSpell(target, SPELL_FLASH_FREEZE_TRAPPED_PLAYER, true);
						caster->ToCreature()->AI()->JustSummoned(c);
					}
				}
				else if (target->GetTypeId() == TYPEID_UNIT)
				{
					if( Creature* c = target->SummonCreature(NPC_FLASH_FREEZE_NPC, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 2000) )
					{
						c->CastSpell(target, SPELL_FLASH_FREEZE_TRAPPED_NPC, true);
						caster->ToCreature()->AI()->JustSummoned(c);
					}
				}
			}
		}

		void Register()
		{
			OnEffectPeriodic += AuraEffectPeriodicFn(spell_hodir_flash_freeze_AuraScript::HandleEffectPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
		}
	};

	SpellScript* GetSpellScript() const
	{
		return new spell_hodir_flash_freeze_SpellScript();
	}

	AuraScript *GetAuraScript() const
	{
		return new spell_hodir_flash_freeze_AuraScript();
	}
};

class spell_hodir_storm_power : public SpellScriptLoader
{
public:
	spell_hodir_storm_power() : SpellScriptLoader("spell_hodir_storm_power") { }

	class spell_hodir_storm_power_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_storm_power_AuraScript)

		void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
		{
			if (Unit* caster = GetCaster())
				if (Aura* a = caster->GetAura(GetId() == SPELL_SHAMAN_STORM_POWER_10 ? SPELL_SHAMAN_STORM_CLOUD_10 : SPELL_SHAMAN_STORM_CLOUD_25))
					a->ModStackAmount(-1);
		}

		void HandleAfterEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
		{
			if (Unit* target = GetTarget())
				if (target->GetTypeId() == TYPEID_PLAYER)
					target->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, GetId(), 0, GetCaster());
		}

		void Register()
		{
			OnEffectApply += AuraEffectApplyFn(spell_hodir_storm_power_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, AURA_EFFECT_HANDLE_REAL);
			AfterEffectApply += AuraEffectApplyFn(spell_hodir_storm_power_AuraScript::HandleAfterEffectApply, EFFECT_0, SPELL_AURA_MOD_CRIT_DAMAGE_BONUS, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK);
		}
	};

	AuraScript *GetAuraScript() const
	{
		return new spell_hodir_storm_power_AuraScript();
	}
};

class spell_hodir_storm_cloud : public SpellScriptLoader
{
public:
	spell_hodir_storm_cloud() : SpellScriptLoader("spell_hodir_storm_cloud") { }

	class spell_hodir_storm_cloud_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_storm_cloud_AuraScript)

		void HandleEffectPeriodic(AuraEffect const * aurEff)
		{
			PreventDefaultAction();
			if (Unit* target = GetTarget())
				target->CastSpell((Unit*)NULL, (GetId() == SPELL_SHAMAN_STORM_CLOUD_10 ? SPELL_SHAMAN_STORM_POWER_10 : SPELL_SHAMAN_STORM_POWER_25), true);
		}

		void Register()
		{
			OnEffectPeriodic += AuraEffectPeriodicFn(spell_hodir_storm_cloud_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
		}
	};

	AuraScript *GetAuraScript() const
	{
		return new spell_hodir_storm_cloud_AuraScript();
	}
};

class achievement_cheese_the_freeze : public AchievementCriteriaScript
{
public:
	achievement_cheese_the_freeze() : AchievementCriteriaScript("achievement_cheese_the_freeze") {}

	bool OnCheck(Player* player, Unit* target)
	{
		return target && target->GetEntry() == NPC_HODIR && target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->AI()->GetData(1);
	}
};

class achievement_getting_cold_in_here : public AchievementCriteriaScript
{
public:
	achievement_getting_cold_in_here() : AchievementCriteriaScript("achievement_getting_cold_in_here") {}

	bool OnCheck(Player* player, Unit* target)
	{
		return target && target->GetEntry() == NPC_HODIR && target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->AI()->GetData(2);
	}
};

class achievement_i_could_say_that_this_cache_was_rare : public AchievementCriteriaScript
{
public:
	achievement_i_could_say_that_this_cache_was_rare() : AchievementCriteriaScript("achievement_i_could_say_that_this_cache_was_rare") {}

	bool OnCheck(Player* player, Unit* target)
	{
		return target && target->GetEntry() == NPC_HODIR && target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->AI()->GetData(3);
	}
};

class achievement_i_have_the_coolest_friends : public AchievementCriteriaScript
{
public:
	achievement_i_have_the_coolest_friends() : AchievementCriteriaScript("achievement_i_have_the_coolest_friends") {}

	bool OnCheck(Player* player, Unit* target)
	{
		return target && target->GetEntry() == NPC_HODIR && target->GetTypeId() == TYPEID_UNIT && target->ToCreature()->AI()->GetData(4);
	}
};

class achievement_staying_buffed_all_winter_10 : public AchievementCriteriaScript
{
public:
	achievement_staying_buffed_all_winter_10() : AchievementCriteriaScript("achievement_staying_buffed_all_winter_10") {}

	bool OnCheck(Player* player, Unit* target)
	{
		return player && player->HasAura(SPELL_MAGE_TOASTY_FIRE_AURA) && player->HasAura(SPELL_DRUID_STARLIGHT_AREA_AURA) && player->HasAura(SPELL_SHAMAN_STORM_POWER_10);
	}
};

class achievement_staying_buffed_all_winter_25 : public AchievementCriteriaScript
{
public:
	achievement_staying_buffed_all_winter_25() : AchievementCriteriaScript("achievement_staying_buffed_all_winter_25") {}

	bool OnCheck(Player* player, Unit* target)
	{
		return player && player->HasAura(SPELL_MAGE_TOASTY_FIRE_AURA) && player->HasAura(SPELL_DRUID_STARLIGHT_AREA_AURA) && player->HasAura(SPELL_SHAMAN_STORM_POWER_25);
	}
};

class spell_hodir_toasty_fire : public SpellScriptLoader
{
public:
	spell_hodir_toasty_fire() : SpellScriptLoader("spell_hodir_toasty_fire") { }

	class spell_hodir_toasty_fire_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_toasty_fire_AuraScript);

		void HandleAfterEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
		{
			if (Unit* target = GetTarget())
				if (target->GetTypeId() == TYPEID_PLAYER)
					target->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, SPELL_MAGE_TOASTY_FIRE_AURA, 0, GetCaster());
		}

		void Register()
		{
			AfterEffectApply += AuraEffectApplyFn(spell_hodir_toasty_fire_AuraScript::HandleAfterEffectApply, EFFECT_0, SPELL_AURA_MOD_STAT, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK);
		}
	};

	AuraScript* GetAuraScript() const
	{
		return new spell_hodir_toasty_fire_AuraScript();
	}
};

class spell_hodir_starlight : public SpellScriptLoader
{
public:
	spell_hodir_starlight() : SpellScriptLoader("spell_hodir_starlight") { }

	class spell_hodir_starlight_AuraScript : public AuraScript
	{
		PrepareAuraScript(spell_hodir_starlight_AuraScript);

		void HandleAfterEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
		{
			if (Unit* target = GetTarget())
				if (target->GetTypeId() == TYPEID_PLAYER)
					target->ToPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, SPELL_DRUID_STARLIGHT_AREA_AURA, 0, GetCaster());
		}

		void Register()
		{
			AfterEffectApply += AuraEffectApplyFn(spell_hodir_starlight_AuraScript::HandleAfterEffectApply, EFFECT_0, SPELL_AURA_MELEE_SLOW, AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK);
		}
	};

	AuraScript* GetAuraScript() const
	{
		return new spell_hodir_starlight_AuraScript();
	}
};

void AddSC_boss_hodir()
{
	new boss_hodir();
	new npc_ulduar_icicle();
	new npc_ulduar_flash_freeze();
	new npc_ulduar_toasty_fire();

	new npc_ulduar_hodir_priest();
	new npc_ulduar_hodir_druid();
	new npc_ulduar_hodir_shaman();
	new npc_ulduar_hodir_mage();

	new spell_hodir_biting_cold_main_aura();
	new spell_hodir_biting_cold_player_aura();
	new spell_hodir_periodic_icicle();
	new spell_hodir_flash_freeze();
	new spell_hodir_storm_power();
	new spell_hodir_storm_cloud();

	new achievement_cheese_the_freeze();
	new achievement_getting_cold_in_here();
	new achievement_i_could_say_that_this_cache_was_rare();
	new achievement_i_have_the_coolest_friends();
	new achievement_staying_buffed_all_winter_10();
	new achievement_staying_buffed_all_winter_25();
	new spell_hodir_toasty_fire();
	new spell_hodir_starlight();
}